- Published on
6 步搞定系统设计面试
- Authors
- Name
- 俞凡
本文梳理了一套通过 6 个步骤清晰展示系统设计思维的应对框架,包括澄清需求、定义成功标准、画出高层架构、设计数据层,到扩展性与可靠性,最后考虑权衡取舍。
系统设计面试并不是考你会不会背各种技术名词,而是看你能不能在有限时间里,有条理的拆解问题、做出合理的架构决策,并把自己的思路讲清楚。
面试的评分标准其实是“思考方式”,而不是“系统有多炫酷”。因此需要一套可重复执行的流程,把几十分钟的面试时间拆分成若干阶段,每一阶段回答一个明确的问题。
接下来就介绍这套能够帮助你顺利通过各种系统设计面试的框架。
50 分钟作战计划
下面是一份 50 分钟时间切片路线图:
- 0–5 分钟:澄清需求
- 6–12 分钟:定义成功标准
- 13–22 分钟:画出高层架构
- 23–32 分钟:设计数据层
- 33–42 分钟:讨论扩展性与可靠性
- 43–50 分钟:收尾与权衡总结
路线图可以按阶段展开,每个阶段都对应面试过程中呈现在白板或文档上的“可见成果”。
阶段 1:先澄清再设计(0–5 分钟)
永远不要直接开始画图。第一步应该是:用问题把“题目”变成“需求”。
可以围绕以下维度澄清:
- 用户与规模:有多少用户、日活?是 100 万还是 10 亿?
- 核心用例:最重要的 1~2 个场景是什么?例如仅做照片分享,还是要包含完整的社交功能?
- 客户端形态:只考虑移动端,还是移动 + Web?
- 地理分布:是否是全球分布,是否有多区域部署需求?
- 时延要求:例如“Feed 打开时间需要控制在 500ms 以内”。
对于面试官抛出的“设计 Instagram”之类的问题,可以先反问:
“我们是只关注图片流(Photo Feed),还是要覆盖整个产品?是否支持视频?大致用户量级是多少?”
这一阶段的目标:用 2–3 分钟让双方对“要构建的东西”达成共识,让后面的设计有清晰边界。
阶段 2:写下什么叫“成功”(6–12 分钟)
在澄清了范围之后,第二步是明确定义功能性和非功能性需求,包括:
- 功能性需求:比如“用户可以上传图片、关注他人、看到关注对象的动态流、对内容点赞与评论”等;
- 非功能性需求:比如“高可用性(High Availability)达到 99.9%”、“Feed 加载延迟(Latency)小于 500ms”、“可以扩展到 1 亿日活用户”、“允许最终一致性(Eventual Consistency)”。
把这些需求点写在白板上或共享文档中,就相当于和面试官形成了“设计合约”:后续所有架构选择,都要能解释清楚“这是为了满足哪条需求”。
这一阶段的目标:让面试官看到你不是在“凭感觉设计”,而是在对齐“什么设计是成功的”。
阶段 3:先画大图,再补细节(13–22 分钟)
到了真正画架构图的时候,强调一个原则:先画大块(High-Level Components),再深入具体实现,而不是一开始就纠结字段、索引或具体中间件。
典型高层架构可以包括:
┌─────────┐
│ Users │
└────┬────┘
│
↓
┌─────────────┐
│ CDN/Cache │
└─────┬───────┘
│
↓
┌──────────────┐ ┌──────────────┐
│ Load Balancer│─────→│ Load Balancer│
└──────┬───────┘ └──────┬───────┘
│ │
↓ ↓
┌─────────────┐ ┌─────────────┐
│ API Servers │ │Media Service│
└──────┬──────┘ └──────┬──────┘
│ │
↓ ↓
┌─────────────┐ ┌─────────────┐
│ Database │ │Object Storage│
└─────────────┘ └─────────────┘
- 客户端(Mobile / Web);
- CDN(Content Delivery Network)和缓存(Cache),用于分发静态资源与热门内容;
- 负载均衡(Load Balancer),把流量分发到后端服务;
- API 服务(API Servers),承载业务逻辑;
- 媒体服务(Media Service),负责图片/视频的处理与存储;
- 数据库(Database),保存用户、关系、元数据;
- 对象存储(Object Storage),保存实际的图片/视频文件。
在讲解数据流时,可以用一句简短的“端到端路径”来串起来,例如:
用户上传图片 → API 服务处理请求 → 媒体服务转码与压缩
↓
写入对象存储
↓
在数据库中记录元数据
↓
返回可访问 URL
这一阶段的目标:让面试官在脑中形成清晰的“系统鸟瞰图”,知道所有关键组件长什么样、怎么互相连接。此时还不必深入到每个组件内部实现。
阶段 4:谈数据,而不是只谈服务(23–32 分钟)
后半段时间建议重点放在“数据层设计”上,因为这最能体现工程判断力。
可以从以下几个维度展开:
- 关系型数据库(SQL)还是非关系型数据库(NoSQL)?
- 用户资料与关注关系这类强一致(ACID)需求高的场景,更适合用 SQL;
- 时间线 / Feed 这类读多写少、允许最终一致性的场景,更适合用可横向扩展的 NoSQL。
- 数据模型与访问模式:
- 例如关注关系可以用
follows表建复合主键,避免重复关注; - Feed 可以预计算并按用户保存为去范式(Denormalized)结构,加快读取。
- 例如关注关系可以用
- 缓存策略:
- 缓存哪些内容:用户资料、热门内容、活跃用户 Feed 等;
- 为什么要缓存:相比直接查数据库,内存缓存(如 Redis)能把几十毫秒的查询压缩到几毫秒,在每秒上万请求的场景下能“挽救”大量数据库资源。
-- SQL 适用于用户与关注关系
CREATE TABLE users (
user_id BIGINT PRIMARY KEY,
username VARCHAR(50) UNIQUE,
created_at TIMESTAMP
);
CREATE TABLE follows (
follower_id BIGINT,
followed_id BIGINT,
created_at TIMESTAMP,
PRIMARY KEY (follower_id, followed_id)
);
// NoSQL (比如 Cassandra) 更适合
{
user_id: "user_123",
feed: [
{post_id: "post_456", timestamp: 1634567890},
{post_id: "post_789", timestamp: 1634567850}
]
}
这一阶段的目标:展示你能够根据访问模式选择合适的存储,并且讲清楚“为什么这样选”以及“放弃了什么”。
阶段 5:把系统放进真实世界(33–42 分钟)
系统上线后会面对流量波动、节点故障、网络抖动等各种现实问题。这个阶段要重点回答两个问题:
- 当流量变成 10 倍时,系统如何扩展?
- 当部分组件失败时,系统如何优雅降级?
可以从以下角度展开:
- 水平扩展:
- 应用服务前增加更多无状态实例,通过负载均衡分发;
- 数据库通过读写分离与只读副本承压。
- 容错与高可用:
- 复制:关键数据多副本存储;
- 熔断器:下游服务异常时快速失败并降级到缓存结果;
- 限流:防止恶意或异常流量;
- 优雅降级:尽量提供“部分可用”的体验,例如主功能可用、部分统计或推荐暂时不可用。
熔断器示例代码:
class CircuitBreaker:
def __init__(self, threshold=5):
self.failures = 0
self.threshold = threshold
self.state = "CLOSED" # CLOSED, OPEN, HALF_OPEN
def call(self, func):
if self.state == "OPEN":
return cached_response()
try:
result = func()
self.failures = 0
return result
except Exception:
self.failures += 1
if self.failures >= self.threshold:
self.state = "OPEN"
raise
上面用简短的伪代码演示了熔断器(Circuit Breaker)如何在失败次数超过阈值时“打开”并立即返回缓存数据,面试中不必照搬代码,但可以用语言说明:自己理解“失败隔离”与“自我恢复”的重要性。
这一阶段的目标:让面试官看到你不仅会“搭系统”,还能放到高并发、高故障率的真实环境里去思考。
阶段 6:干净利落的收尾(43–50 分钟)
最后 5~7 分钟,重点不是继续加新组件,而是:
- 用 30~60 秒复述你的整体方案:
- 系统主干架构;
- 关键技术选择(例如 SQL 用在用户与关系,NoSQL 用在 Feed,与 CDN 配合做全局分发);
- 如何扩展与保证可靠性。
- 主动点出几项关键权衡:
- 比如“用 NoSQL 做 Feed,换来快速读取与易扩展,但牺牲了一些查询灵活性与强一致性”;
- “预计算 Feed 提升打开速度,但增加了存储开销以及可能短时间内呈现旧数据的风险”。
- 抛出开放性问题:
- 例如:“如果需要,我可以进一步深入某个组件,比如 Feed 生成策略或多区域容灾,您更希望听哪一块?”
这一阶段的目标:
- 把零散的讨论收拢成结构清晰的故事;
- 让面试官感到“即使时间到了,这个人依然在有条理的思考权衡,而不是随意堆砌技术名词”。
真正的秘诀
系统设计面试中不需要做的事情:
- 不必一上来就报一堆云服务的品牌名;
- 不必急着切成复杂的微服务;
- 不必在一开始就画出所有细节;
- 不必给出“这个就是最佳方案”的结论。
相反,更重要的是:
- 从澄清问题开始,而不是从方案开始;
- 按阶段逐步搭建系统,而不是一口气抛出完整架构图;
- 所有选择都有理由,能讲出“为什么这样设计”;
- 诚实面对权衡,承认每个选择都有利有弊;
- 保持对话,主动和面试官互动,而不是独角戏式的画完就走。
这也是为什么同一套技术栈,在不同候选人嘴里,呈现出的“成熟度”会完全不同:真正拉开差距的是“解释方案的方式”和“面对不确定性的态度”。
行动清单
下面是一份非常务实的练习建议,简要整理成可执行清单:
- 选 5 个不同的系统设计题,用这套框架完整走一遍;
- 给自己计时,习惯在压力下也能按阶段推进;
- 录下自己的讲解过程,回看时关注“哪里讲得不清楚、哪里跳步太快”;
- 在练习中刻意练习“讲清楚权衡”的能力,而不是背标准答案;
- 面试时记住:对方要看的,是思考路径与沟通能力,而不是一张完美无缺的架构图。
要点回顾
- 系统设计面试考察的是结构化思维与沟通,而不是技术名词堆砌。
- 在面试过程中,可以用“澄清需求 → 定义成功 → 画大图 → 设计数据层 → 讨论扩展性与可靠性 → 收尾与权衡”这六个阶段来组织自己的输出。
- 数据层设计是展现工程判断的关键环节,要能结合访问模式解释 SQL / NoSQL、缓存与预计算等选择。
- 讨论扩展性与可靠性时,应从水平扩展、复制、限流、熔断与优雅降级等角度说明“系统如何在真实世界中生存”。
- 收尾阶段用简短复盘与权衡总结,把整场讨论串成一个完整故事,并主动邀请面试官选择可以进一步深入的部分。